Pythonで選挙データを分析してみよう!⑤〜特定候補者・政党の徹底分析編〜
このシリーズでは、2025/07/21に行われた参院選挙結果を分析するためのプログラムを、一つずつ紐解いて解説していきます。
選挙データ分析シリーズ第5弾、いよいよ分析編に突入です。
作成したコードは Github に公開しているので、興味があれば、見てみてください。
※コードは、予告なく、改変/削除されることがあります。
これまでは、人間には読みやすいけれど機械には優しくない、様々なフォーマットのデータを綺麗に整形してきました。今回はその集大成として、整形した複数のデータを組み合わせて、特定の候補者・政党を徹底的に分析するスクリプト team_mirai_detailed_analysis.py を見ていきましょう!
目標は「データ」から「インサイト」へ
これまでの作業で、私たちはバラバラだったデータを、行と列が整然と並んだ「使える」データに変換してきました。今回の目的は、その綺麗なデータを使って、単なる集計を超えた「インサイト(洞察)」を得ることです。
分析対象は、選挙区の「武藤 かず子」候補と、比例代表の「チームみらい」です。このスクリプトが、どのような手法で分析を深掘りしていくのか、そのプロセスを追っていきましょう。
Step 1: 分析の土台作り – 整形済みデータの集結
何よりもまず、これまでのシリーズで綺麗にしてきたデータを読み込みます。
def load_candidate_data():
"""候補者データを読み込む"""
df = pd.read_csv('output/候補者別市区町村別得票調べ_縦並び版.csv', encoding='utf-8')
return df
def load_voting_results():
"""投票結果データを読み込む"""
df = pd.read_csv('output/市区町村別開票結果調べ_縦並び版.csv', encoding='utf-8')
return df
def load_proportional_data():
"""比例データを読み込む"""
proportional_df = pd.read_csv('output_proportional/比例代表_得票総数の開票区別政党等別一覧_縦並び版.csv', encoding='utf-8')
results_df = pd.read_csv('output_proportional/比例代表_開票結果調べ_縦並び版.csv', encoding='utf-8')
return proportional_df, results_df
# ... main関数内 ...
candidate_data = load_candidate_data()
voting_results = load_voting_results()
proportional_candidate_data, proportional_results_data = load_proportional_data()Step 2: パフォーマンス分析 – 得票率を算出して評価する
総得票数や平均得票数を見るだけでは、本当の実力は見えてきません。有権者数が多い都市部で票が多くなるのは当然だからです。そこで重要になるのが「得票率」です。
このスクリプトでは、候補者の得票データと、市区町村別の有効投票総数データをマージ(結合)することで、市区町村ごとの得票率を算出しています。
def analyze_team_mirai_performance(team_mirai_data, voting_results, voting_info):
# ...
# 「市区町村名」をキーにして、2つのデータフレームを結合
merged = team_mirai_data.merge(voting_results, on='市区町村名', how='left')
# 結合したデータを使って、得票率を計算
if '有効投票総数' in merged.columns:
merged['得票率'] = (merged['得票数'] / merged['有効投票総数']) * 100
analysis['avg_vote_rate'] = merged['得票率'].mean()
# ...
# 得票率に基づいて地域を分類
analysis['high_performance_regions'] = merged[merged['得票率'] >= 3.0]['市区町村名'].tolist()
analysis['medium_performance_regions'] = merged[(merged['得票率'] >= 2.0) & (merged['得票率'] < 3.0)]['市区町村名'].tolist()
analysis['low_performance_regions'] = merged[merged['得票率'] < 2.0]['市区町村名'].tolist()
return analysis, mergedpd.merge を使って、候補者の得票数と有効投票数を同じ行に並べるのがポイントです。これにより、得票数 ÷ 有効投票総数 という計算が簡単に行えるようになります。 さらに、算出した得票率を使って「3%以上を高パフォーマンス地域」のように独自の基準で地域を分類し、分析を深めています。
Step 3: 地域性の分析 – 市と町、それぞれの強みを探る
得票には地域性があるはずです。このスクリプトでは、市区町村名に「市」が含まれるかどうかで、データを「市部」と「町村部」に分けて集計しています。
def analyze_regional_patterns(team_mirai_data):
patterns = {}
# さいたま市内の分析
saitama_regions = team_mirai_data[team_mirai_data['市区町村名'].str.contains('さいたま市', na=False)]
# ...
# 市部と町村部の比較
city_regions = team_mirai_data[team_mirai_data['市区町村名'].str.contains('市', na=False)]
town_regions = team_mirai_data[~team_mirai_data['市区町村名'].str.contains('市', na=False)]
if not city_regions.empty:
patterns['city_total_votes'] = city_regions['得票数'].sum()
patterns['city_avg_votes'] = city_regions['得票数'].mean()
# ...
return patternspandasの文字列処理機能 .str.contains('キーワード') を使うことで、このように柔軟なデータ抽出が可能です。「さいたま市内ではどうだったか?」「市と町ではどちらに強いのか?」といった、より具体的な問いに答えることができます。
Step 4: 合わせ技! – 選挙区と比例のデータを比較する
この分析の最も面白い部分が、選挙区での個人の得票と、比例代表での政党の得票を比較している点です。
def generate_team_mirai_report(analysis, patterns, team_mirai_data, merged_data=None, proportional_analysis=None):
# ...
# 選挙区と比例の比較分析
if 'overall_vote_rate' in analysis and 'overall_rate' in proportional_analysis:
report.append(f"\n【選挙区 vs 比例代表 比較分析】")
report.append(f"選挙区での得票率: {analysis['overall_vote_rate']:.2f}%")
report.append(f"比例代表での得票率: {proportional_analysis['overall_rate']:.2f}%")
diff = proportional_analysis['overall_rate'] - analysis['overall_vote_rate']
if diff > 0:
report.append(f"比例の方が{diff:.2f}ポイント高い")
else:
report.append(f"選挙区の方が{abs(diff):.2f}ポイント高い")
# ...レポート生成の最終段階で、それぞれで計算した全体の得票率や総得票数を並べて比較しています。これにより、「候補者個人の人気が政党の人気を上回っているのか、それともその逆か」といった、非常に興味深いインサイトを得ることができます。
まとめ
今回は、整形済みの複数のデータを組み合わせることで、特定の候補者や政党に関する多角的な分析を行う方法を見てきました。
- 分析の基本は、複数のデータを結合して新しい指標(今回は得票率)を作ること。
- 文字列処理などを活用して、地域性などの特徴を切り出して比較する。
- 異なる種類のデータ(選挙区と比例)を比較することで、より深い洞察を得る。
データを綺麗にする作業は地道ですが、その先にはこういった面白い分析の世界が待っています。まさにデータ分析の醍醐味ですね。





